package com.bms.cfcmms.model;

import com.bms.cfcmms.core.util.Utils;
import com.bms.cfcmms.util.annotation.TableDefine;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.util.StringUtils;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import javax.persistence.Column;

/**
 * Created by carl on 2016/4/12.
 */
@Data
public class BaseModel implements RowMapper, Serializable {

    private static final Logger log = LoggerFactory.getLogger(BaseModel.class);

    private Integer start;

    private Integer limit;

    /**
     * 生成bean对象.
     * <p>
     * <p>重载RowMapper中的方法，方面查询后能够生成相应的bean实体,<br>
     * 利用java反射的技术来实现的
     * </p>
     *
     * @param resultSet 返回结果集
     * @param index     结果集中记录的索引
     * @return 当前结果对应的实体对象
     * @throws SQLException SQL异常
     */
    @Override
    public Object mapRow(ResultSet resultSet, int index) throws SQLException {
        Object object = null;
        try {
            object = (Object) this.getClass().newInstance();
            Map<String, List<Field>> coloumns = Utils.findAllColoumns(this.getClass());
            for (Field field : coloumns.get(Utils.COMM_COLOUMNS)) {
                String columnName = findRealColumnName(field);
                dealValue(resultSet, object, field, columnName);
            }

            for (Field field : coloumns.get(Utils.JOIN_COLOUMNS)) {
                Object obj = getObject(resultSet, field);
                if (obj == null) {
                    continue;
                }
                field.set(object, obj);
            }
        } catch (InstantiationException e) {
            log.error("", e);
        } catch (IllegalAccessException e) {
            log.error("", e);
        } catch (NoSuchFieldException e) {
            log.error("", e);
        } catch (IntrospectionException e) {
            log.error("", e);
        } catch (InvocationTargetException e) {
            log.error("", e);
        }
        return object;
    }

    private Object getObject(ResultSet resultSet, Field field)
            throws InstantiationException, IllegalAccessException, SQLException, NoSuchFieldException {
        Object obj = field.getType().newInstance();
        field.setAccessible(true);
        Annotation[] tableDefines = obj.getClass().getDeclaredAnnotations();
        TableDefine tableDefine = null;
        for (Annotation annotation : tableDefines) {
            if (annotation instanceof TableDefine) {
                tableDefine = (TableDefine) annotation;
                break;
            }
        }
        if (tableDefine !=null) {
            String primaryKey = tableDefine.primaryKey();
            Object objects = resultSet.getObject(primaryKey);
            if (objects == null) {
                return null;
            }
            Field subField = obj.getClass().getDeclaredField(primaryKey);
            subField.setAccessible(true);
            subField.set(obj, resultSet.getObject(primaryKey));
        }
        return obj;
    }

    private void dealValue(ResultSet resultSet, Object object, Field field, String columnName)
            throws SQLException, IntrospectionException, IllegalAccessException, InvocationTargetException {
        Object value;
        String classFullNasme = field.getType().getName();
        if ("java.lang.Integer".equalsIgnoreCase(classFullNasme)) {
            value = resultSet.getInt(columnName);
        } else if ("java.lang.Long".equalsIgnoreCase(classFullNasme)) {
            value = resultSet.getLong(columnName);
        } else if ("java.sql.Timestamp".equalsIgnoreCase(classFullNasme)) {
            value = resultSet.getTimestamp(columnName);
        } else if ("java.lang.String".equalsIgnoreCase(classFullNasme)) {
            value = resultSet.getString(columnName);
        } else {
            value = resultSet.getObject(columnName);
        }

        PropertyDescriptor pd = new PropertyDescriptor(field.getName(), this.getClass());
        Method setMethond = pd.getWriteMethod();//获得写方法
        setMethond.invoke(object, value);
    }

    private String findRealColumnName(Field field) {
        String columnName;
        Column column = field.getAnnotation(Column.class);
        if (column != null) {
            if (StringUtils.hasLength(column.name())) {
                columnName = column.name();
            } else {
                columnName = field.getName();
            }
        } else {
            columnName = field.getName();
        }
        return columnName;
    }
}
